package valid

import (
	"reflect"
	"sync"

	"github.com/gin-gonic/gin"

	"github.com/gin-gonic/gin/binding"
	zh "github.com/go-playground/locales/zh_Hans"
	ut "github.com/go-playground/universal-translator"
	validator "gopkg.in/go-playground/validator.v9"
	zh_translations "gopkg.in/go-playground/validator.v9/translations/zh"
)

// CustomValid  自定义校验接口
type CustomValid interface {
	CustomValid() error
}

// Key 翻译器关键字
const key = "__trans__"

// FromContext 翻译器
func FromContext(c *gin.Context) ut.Translator {
	if t, ok := c.Get(key); ok {
		if t1, ok := t.(ut.Translator); ok {
			return t1
		}
	}
	return nil
}

// Translator 校验翻译中间件
func Translator() gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Set(key, trans)
	}
}

var trans ut.Translator

func init() {
	zh := zh.New()
	uni := ut.New(zh, zh)

	// this is usually know or extracted from http 'Accept-Language' header
	// also see uni.FindTranslator(...)
	trans, _ = uni.GetTranslator("zh")

	binding.Validator = new(defaultValidator)
}

type defaultValidator struct {
	once     sync.Once
	validate *validator.Validate
}

var _ binding.StructValidator = &defaultValidator{}

func (v *defaultValidator) ValidateStruct(obj interface{}) error {

	if kindOfData(obj) == reflect.Struct {

		v.lazyinit()

		if err := v.validate.Struct(obj); err != nil {
			return error(err)
		}
		if cv, ok := obj.(CustomValid); ok {
			return cv.CustomValid()
		}
	}

	return nil
}

func (v *defaultValidator) Engine() interface{} {
	v.lazyinit()
	return v.validate
}

func (v *defaultValidator) lazyinit() {
	v.once.Do(func() {

		v.validate = validator.New()
		zh_translations.RegisterDefaultTranslations(v.validate, trans)
		v.validate.SetTagName("binding")

		// add any custom validations etc. here
	})
}

func kindOfData(data interface{}) reflect.Kind {

	value := reflect.ValueOf(data)
	valueType := value.Kind()

	if valueType == reflect.Ptr {
		valueType = value.Elem().Kind()
	}
	return valueType
}
